﻿/*
	TinyMCE for ExtJS 4.x
*/
(function() {
	var tmceInitialized = false,
	// Lazy references to classes. To be filled in the initTinyMCE method.
	WindowManager,ControlManager;

	// Create a new Windows Group for the dialogs
	/*var windowGroup = new Ext.WindowGroup();
	windowGroup.zseed = 12000;*/


	/** ----------------------------------------------------------
	Ext.ux.TinyMCE
	*/
	 Ext.define('Ext.ux.TinyMCE', {
        extend: 'Ext.form.field.Text',
        alias: 'widget.tinymce',
			// TinyMCE Settings specified for this instance of the editor.
		tinymceSettings: null,

		// Validation properties
		allowBlank: true,
		fieldSubTpl: [
          '<div id="{id}"',
          '<tpl if="name">name="{name}" </tpl>',
          '<tpl if="size">size="{size}" </tpl>',
          'class="{fieldCls}">',
          '<div></div>',
          '</div>',
          {
              compiled: true,
              disableFormats: true
          }
        ],

		/** ----------------------------------------------------------
		*/
		constructor: function(cfg) {

			var config = {
                tinymceSettings:{}
            };
            
            Ext.apply(config, cfg);
            
           	Ext.applyIf(config.tinymceSettings,tinyMCE.settings);
            
            

            // Add events
            this.addEvents({
                "editorcreated": true
            });

            this.callParent([config]); //Ext4
		},

		/** ----------------------------------------------------------
		*/
		initComponent: function() {
			this.callParent(); // Ext4: added
			this.tinymceSettings = this.tinymceSettings || {};
			Ext.ux.TinyMCE.initTinyMCE({ language: this.tinymceSettings.language });
		},

		/** ----------------------------------------------------------
		
		initEvents: function() {
			this.originalValue = this.getValue();
		},
		*/
		/** ----------------------------------------------------------
		*/
		onRender: function(ct, position) {
			this.callParent(arguments); // Ext4: added (makes this.inputEl available)

			// Fix size if it was specified in config
			if (Ext.type(this.width) == "number") {this.tinymceSettings.width = this.width;}
			if (Ext.type(this.height) == "number") {this.tinymceSettings.height = this.height;}

			/*
			//this.el.dom.style.border = '0 none';
			this.el.dom.setAttribute('tabIndex', -1);
			this.el.addCls('x-hidden');
			
			// Wrap textarea into DIV
			this.textareaEl = this.el;
			var wrapElStyle = { overflow: "hidden" };
			if( Ext.isIE ) { // fix IE 1px bogus margin
				wrapElStyle["margin-top"] = "-1px";
				wrapElStyle["margin-bottom"] = "-1px";
			}
			this.wrapEl = this.el.wrap({ style: wrapElStyle });
			this.actionMode = "wrapEl"; // Set action element to the new wrapper
			this.positionEl = this.wrapEl;
			
			var id = this.getId();
			*/
			 var divForTinyEl = this.inputEl.first(),
                id = Ext.id(divForTinyEl);
			// Create TinyMCE editor.
			this.ed = new tinymce.Editor(id, this.tinymceSettings);
			
			// Validate value onKeyPress
			var validateContentTask = new Ext.util.DelayedTask( this.validate, this );
			
			this.ed.onKeyPress.add(Ext.bind(function(ed, controlManager) {
                validateContentTask.delay( 250 );
            }, this));

			// Set up editor events' handlers
			 this.ed.onBeforeRenderUI.add(Ext.bind(function(ed, controlManager) {
                // Replace control manager
                ed.controlManager = new ControlManager(this, ed);
            }, this));

			this.ed.onPostRender.add(Ext.bind(function(ed, controlManager) {
				var s = ed.settings;

				// Modify markup.
				var tbar = Ext.get(Ext.DomQuery.selectNode("#" + this.ed.id + "_tbl td.mceToolbar"));
				if( tbar != null ) {
                    // If toolbar is present
                    var tbars = tbar.select("> table.mceToolbar");
                    Ext.core.DomHelper
                        .append( tbar,
                            { tag: "div", id: this.ed.id + "_xtbar", style: { overflow: "hidden"} }
                            , true )
                        .appendChild(tbars);
                }
				
				// Change window manager
				ed.windowManager = new WindowManager({
					control: this,
					editor: this.ed,
					manager: this.manager
				});
				// Patch css-style for validation body like ExtJS
				Ext.get(ed.getContentAreaContainer()).addCls('patch-content-body'); // Ext4 code

				// Event of focused body
				Ext.Element.fly(s.content_editable ? ed.getBody() : ed.getWin())
					.on("focus", this.onFocus, this);

				// Event of blur body
				Ext.Element.fly(s.content_editable ? ed.getBody() : ed.getWin())
					.on("blur", this.onBlur, this,
						this.inEditor && Ext.isWindows && Ext.isGecko ? { buffer: 10} : null
					);

			},this));

			// Set event handler on editor init.
			//this.ed.onInit.add(function() {
			//} .createDelegate(this));

			// Wire events			
			this.ed.onChange.add(Ext.bind(function(ed, l) {
                this.fireEvent("change", ed, l);
            }, this));

			//this.ed.onChange.add( fireChangeEvent );
			//this.ed.onUndo.add( fireChangeEvent );
			//this.ed.onRedo.add( fireChangeEvent );

			this.ed.onKeyDown.add(Ext.bind(function(ed, e) {
				this.fireEvent("keydown", this, Ext.EventObject );
			} ,this));

			this.ed.onKeyUp.add(Ext.bind(function(ed, e) {
				this.fireEvent("keyup", this, Ext.EventObject );
			} ,this));

			this.ed.onKeyPress.add(Ext.bind(function(ed, e) {
				this.fireEvent("keypress", this, Ext.EventObject );
			},this));

			// Render the editor
			this.ed.render();
			tinyMCE.add(this.ed);
			
			// Fix editor size when control will be visible
			Ext.Function.defer(function fixEditorSize() {
			
				// If element is not visible yet, wait.
				if( !this.isVisible() ) {
					Ext.defer(arguments.callee, 50, this );
					return;
				}
				
				var size = this.el.getStyleSize();
				this.withEd( function() {
					//console.log(this.value);
					this._setEditorSize( size.width, size.height );
					
					// Indicate that editor is created
					this.fireEvent("editorcreated");
				});
			},250,this);
		},

		/** ----------------------------------------------------------
		*/
		getResizeEl: function() {
			return this.inputEl;
		},



		initValue: function() {

            if (!this.rendered)
                this.callParent();
            else {
                if (this.value !== undefined) {
                    this.setValue(this.value);
                }
                else {
                	
                    // Ext4: Don't know how to handle this, there is no hidden textarea like in Ext3 implementation
                    throw "Ext.ux.TinyMCE.initValue: No initial value available";
                    /* This is the code from Ext3 implementation
                    var v = this.textareaEl.value; 
                    if ( v )
                        this.setValue( v );*/
                }
            }
        },

        /** ----------------------------------------------------------
        */
        beforeDestroy: function() {
            if( this.ed ) tinyMCE.remove( this.ed );
            if( this.inputEl ) Ext.destroy( this.inputEl ); //Ext4
            this.callParent(); // Ext4
        },

		/** ----------------------------------------------------------
		*/
		getRawValue : function(){

			if( !this.rendered || !this.ed.initialized )
				return Ext.value( this.value, '' );
				
			var v = this.ed.getContent();
			if(v === this.emptyText){
				v = '';
			}
			return v;
		},

		/** ----------------------------------------------------------
		*/
		getValue: function() {

			if( !this.rendered || !this.ed.initialized )
				return Ext.value( this.value, '' );

			var v = this.ed.getContent();
			if( v === this.emptyText || v === undefined ){
				v = '';
			}
			return v;
		},

		/** ----------------------------------------------------------
		*/
		/*
		setRawValue: function(v) {
			this.value = v;
			if (this.rendered)
				this.withEd(function() {
					this.ed.undoManager.clear();
					this.ed.setContent(v === null || v === undefined ? '' : v);
					this.ed.startContent = this.ed.getContent({ format: 'raw' });
				});
		},
		*/
		/** ----------------------------------------------------------
		*/
		setValue: function(v) {

			this.value = v;

			if (!this.ed || !this.ed.initialized)
				this.withEd(function() {
					this.ed.undoManager.clear();
					this.ed.setContent(v === null || v === undefined ? '' : v);
					this.ed.startContent = this.ed.getContent({ format: 'raw' });
					this.validate();
					//this.ed.resizeToContent();
				});
		},

		/** ----------------------------------------------------------
		*/
		isDirty: function() {
			if (this.disabled || !this.rendered) {
				return false;
			}
			return this.ed && this.ed.initialized && this.ed.isDirty();
		},

		/** ----------------------------------------------------------
		*/
		syncValue: function() {
			if (this.rendered && this.ed.initialized)
				this.ed.save();
		},

		/** ----------------------------------------------------------
		*/
		getEd: function() {
			return this.ed;
		},

		/** ----------------------------------------------------------
		*/
		disable: function() {
			this.withEd(function() {
				var bodyEl = this.ed.getBody();
				bodyEl = Ext.get(bodyEl);

				if (bodyEl.hasCls('mceContentBody')) {
					bodyEl.removeCls('mceContentBody');
					bodyEl.addCls('mceNonEditable');
				}
			});
			this.callParent(arguments);
		},

		/** ----------------------------------------------------------
		*/
		enable: function() {
			this.withEd(function() {
				var bodyEl = this.ed.getBody();
				bodyEl = Ext.get(bodyEl);

				if (bodyEl.hasCls('mceNonEditable')) {
					bodyEl.removeCls('mceNonEditable');
					bodyEl.addCls('mceContentBody');
				}
			});
			this.callParent(arguments);
		},

		afterComponentLayout:function()
		{
			var ret = this.callParent(arguments);
			
			var size = this.el.getStyleSize();			
			if( this.rendered && this.isVisible() ) {
				this.withEd(function() { this._setEditorSize( size.width, size.height ); });
			}
			return ret;
		},
		/** ----------------------------------------------------------
		*/
		onResize: function(aw, ah) {

			if( Ext.type( aw ) != "number" ){
				aw = this.getWidth();
			}
			if( Ext.type(ah) != "number" ){
				ah = this.getHeight();
			}
			if (aw == 0 || ah == 0)
				return;

			if( this.rendered && this.isVisible() ) {
				this.withEd(function() { this._setEditorSize( aw, ah ); });
			}
		},
		
		/** ----------------------------------------------------------
			Sets control size to the given width and height
		*/
		_setEditorSize: function( width, height ) {
		
			// We currently support only advanced theme resize
			if( !this.ed.theme.AdvancedTheme ) return;
		
			// Minimal width and height for advanced theme
			if( width < 100 ) width = 100;
			if( height < 129 ) height = 129;
			
			// Set toolbar div width
			var id = this.ed.id;

			var edTable = Ext.get(id + "_tbl"), 
				edIframe = Ext.get(id + "_ifr"),
				edToolbar = Ext.get(id+"_toolbargroup");

			var toolbarWidth = width;
			if( edTable ) 
				toolbarWidth = width - edTable.getFrameWidth( "lr" );		
			
			var toolbarHeight = 0;
			if( edToolbar ) {
				toolbarHeight = edToolbar.getHeight();
				var toolbarTd = edToolbar.findParent( "td", 5, true );
				toolbarHeight += toolbarTd.getFrameWidth( "tb" );
				edToolbar.setWidth( toolbarWidth );
			}
			
			var edStatusbarTd = edTable.down( "td.mceStatusbar" );
			
			var statusbarHeight = 0;
			if( edStatusbarTd ) {
				statusbarHeight += edStatusbarTd.getHeight();
			}
			
			var iframeHeight = height - toolbarHeight - statusbarHeight;
			
			var iframeTd = edIframe.findParent( "td", 5, true );
			if( iframeTd )
				iframeHeight -= iframeTd.getFrameWidth( "tb" );
				
			// Resize iframe and container
			edTable.setSize( width, iframeHeight );
			edIframe.setSize( toolbarWidth, iframeHeight );
		},

		/** ----------------------------------------------------------
		*/
		focus: function(selectText, delay) {
			if (delay) {
				Ext.defer(this.focus,typeof delay == 'number' ? delay : 10, this, [selectText, false]);
				return;
			}

			this.withEd(function() {
				this.ed.focus();
				/*if (selectText === true) {
				// TODO: Select editor's content
				}*/
			});

			return this;
		},

		/** ----------------------------------------------------------
		*/
		processValue : function( value ){
			return Ext.util.Format.stripTags( value );
		},
		
		/** ----------------------------------------------------------
		*/
		validateValue: function( value ) {
			if(Ext.isFunction(this.validator)){
				var msg = this.validator(value);
				if(msg !== true){
					this.markInvalid(msg);
					return false;
				}
			}
			if(value.length < 1 || value === this.emptyText){ // if it's blank
				 if(this.allowBlank){
					 this.clearInvalid();
					 return true;
				 }else{
					 this.markInvalid(this.blankText);
					 return false;
				 }
			}
			if(value.length < this.minLength){
				this.markInvalid(String.format(this.minLengthText, this.minLength));
				return false;
			}
			if(value.length > this.maxLength){
				this.markInvalid(String.format(this.maxLengthText, this.maxLength));
				return false;
			}	
			if(this.vtype){
				var vt = Ext.form.VTypes;
				if(!vt[this.vtype](value, this)){
					this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
					return false;
				}
			}
			if(this.regex && !this.regex.test(value)){
				this.markInvalid(this.regexText);
				return false;
			}
			return true;
		},

		/** ----------------------------------------------------------
		If ed (local editor instance) is already initilized, calls
		specified function directly. Otherwise - adds it to ed.onInit event.
		*/
		withEd: function(func) {

			// If editor is not created yet, reschedule this call.
			if (!this.ed) this.on(
				"editorcreated",
				function() { this.withEd(func); },
				this);

			// Else if editor is created and initialized
			else if (this.ed.initialized) func.call(this);

			// Else if editor is created but not initialized yet.
			else this.ed.onInit.add(Ext.bind(function() { Ext.defer(func,10, this); } ,this));
		},

		statics: {

			/**
			Static field with all the plugins that should be loaded by TinyMCE.
			Should be set before first component would be created.
			@static
			*/
			tinymcePlugins: "pagebreak,style,layer,table,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
	
			/** ----------------------------------------------------------
				Inits TinyMCE and other necessary dependencies.
			*/
			initTinyMCE: function(settings) {
				if (!tmceInitialized) {
	
					// Create lazy classes
					/** ----------------------------------------------------------
					WindowManager
					*/
					WindowManager = Ext.extend( tinymce.WindowManager, {
	
						// Reference to ExtJS control Ext.ux.TinyMCE.
						control: null,
	
						/** ----------------------------------------------------------
							Config parameters:
							control - reference to Ext.ux.TinyMCE control
							editor - reference to TinyMCE intstance.
							mangager - WindowGroup to use for the popup window. Could be empty.
						*/
						constructor: function( cfg ) {
							WindowManager.superclass.constructor.call(this, cfg.editor);
	
							// Set reference to host control
							this.control = cfg.control;
	
							// Set window group
							this.manager = cfg.manager;  
						},
	
						/** ----------------------------------------------------------
						*/
						alert: function(txt, cb, s) {
							Ext.MessageBox.alert("", this.editor.getLang(txt,txt), function() {
								if (!Ext.isEmpty(cb)) {
									cb.call(this);
								}
							}, s);
						},
	
						/** ----------------------------------------------------------
						*/
						confirm: function(txt, cb, s) {
							Ext.MessageBox.confirm("", this.editor.getLang(txt,txt), function(btn) {
								if (!Ext.isEmpty(cb)) {
									cb.call(this, btn == "yes");
								}
							}, s);
						},
	
						/** ----------------------------------------------------------
						*/
						open: function(s, p) {
	
							this.control._unbindBlurAndFocus();
	
							s = s || {};
							p = p || {};
	
							if (!s.type)
								this.bookmark = this.editor.selection.getBookmark('simple');
	
							s.width = parseInt(s.width || 320);
							s.height = parseInt(s.height || 240) + (tinymce.isIE ? 8 : 0);
							s.min_width = parseInt(s.min_width || 150);
							s.min_height = parseInt(s.min_height || 100);
							s.max_width = parseInt(s.max_width || 2000);
							s.max_height = parseInt(s.max_height || 2000);
							s.movable = true;
							s.resizable = true;
							p.mce_width = s.width;
							p.mce_height = s.height;
							p.mce_inline = true;
	
							this.features = s;
							this.params = p;
	
							var win = new Ext.Window(
							{
								title: s.name,
								width: s.width,
								height: s.height,
								minWidth: s.min_width,
								minHeight: s.min_height,
								resizable: true,
								maximizable: s.maximizable,
								minimizable: s.minimizable,
								modal: true,
								stateful: false,
								constrain: true,
								manager: this.manager,
								layout: "fit",
								items: [
									new Ext.Component({
										autoEl: {
											tag: 'iframe',
											src: s.url || s.file
										},
										style : 'border-width: 0px;'
									})
								],
								listeners: {
									beforeclose: function() {
										this.control._bindBlurAndFocus();
									},
									scope: this
								}
							});
	
							p.mce_window_id = win.getId();
	
							win.show(null,
								function() {
									if (s.left && s.top)
										win.setPagePosition(s.left, s.top);
									var pos = win.getPosition();
									s.left = pos[0];
									s.top = pos[1];
									this.onOpen.dispatch(this, s, p);
								},
								this
							);
							this.lastWin = win;
							return win;
						},
	
						/** ----------------------------------------------------------
						*/
						close: function(win) {
	
							// Probably not inline
							if (!win.tinyMCEPopup || !win.tinyMCEPopup.id) {
								WindowManager.superclass.close.call(this, win);
								return;
							}
	
							var w = Ext.getCmp(win.tinyMCEPopup.id);
							if (w) {
								this.onClose.dispatch(this);
								w.close();
							}
						},
	
						/** ----------------------------------------------------------
						*/
						setTitle: function(win, ti) {
	
							// Probably not inline
							if (!win.tinyMCEPopup || !win.tinyMCEPopup.id) {
								//if(typeof(WindowManager.superclass.setTitle)=='function'){
									Ext.window.Window.superclass.setTitle.call(this,  ti);
								return;
							}
	
							var w = Ext.getCmp(win.tinyMCEPopup.id);
							if (w) w.setTitle(ti);
						},
	
						/** ----------------------------------------------------------
						*/
						resizeBy: function(dw, dh, id) {
	
							var w = Ext.getCmp(id);
							if (w) {
								var size = w.getSize();
								w.setSize(size.width + dw, size.height + dh);
							}
						},
	
						/** ----------------------------------------------------------
						*/
						focus: function(id) {
							var w = Ext.getCmp(id);
							if (w) w.setActive(true);
						}
						
					});
	
					/** ----------------------------------------------------------
					ControlManager
					*/
					ControlManager = Ext.extend( tinymce.ControlManager, {
	
						// Reference to ExtJS control Ext.ux.TinyMCE.
						control: null,
	
						/** ----------------------------------------------------------
						*/
						constructor: function(control, ed, s) {
							this.control = control;
							ControlManager.superclass.constructor.call(this, ed, s);
						},
	
						/** ----------------------------------------------------------
						*/
						createDropMenu: function(id, s) {
							// Call base method
							var res = ControlManager.superclass.createDropMenu.call(this, id, s);
	
							// Modify returned result
							var orig = res.showMenu;
							res.showMenu = function(x, y, px) {
								orig.call(this, x, y, px);
								Ext.fly('menu_' + this.id).setStyle("z-index", 200001);
							};
	
							return res;
						},
	
						/** ----------------------------------------------------------
						*/
						createColorSplitButton: function(id, s) {
							// Call base method
							var res = ControlManager.superclass.createColorSplitButton.call(this, id, s);
	
							// Modify returned result
							var orig = res.showMenu;
							res.showMenu = function(x, y, px) {
								orig.call(this, x, y, px);
								Ext.fly(this.id + '_menu').setStyle("z-index", 200001);
							};
	
							return res;
						}
					});
	
					// Init TinyMCE
					var s = {
						mode: "none",
						plugins: Ext.ux.TinyMCE.tinymcePlugins,
						theme: "advanced",
						accessibility_focus:false,
						theme_advanced_resizing: false
					};
					Ext.apply(s, settings);
					
					if (!tinymce.dom.Event.domLoaded)
						tinymce.dom.Event._pageInit();
	
					tinyMCE.init(s);
					tmceInitialized = true;
				}
			}
		},
		/** ----------------------------------------------------------
			internal
			Unbind blur and focus events coming from DOM node. Used while opening aux windows.
		*/
		_unbindBlurAndFocus: function() {
			//this.onBlur = Ext.emptyFn;
			/*this.mun( this.el, 'focus' );
			this.mun( this.el, 'blur' );*/
			Ext.util.Observable.capture( this, function() { return false; } );
		},

		/** ----------------------------------------------------------
			internal
			Bind blur and focus events coming from DOM. Used to return back normal blur and focus opearaions.
			Code is copied from initEvents method.
		*/
		_bindBlurAndFocus: function() {
			this.ed.focus();
			Ext.util.Observable.releaseCapture( this );
			
			/*this.mon(this.el, 'focus', this.onFocus, this);

			// standardise buffer across all browsers + OS-es for consistent event order.
			// (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
			this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);*/
		}
	});

	Ext.ComponentMgr.registerType("tinymce", Ext.ux.TinyMCE);

})();